home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / e.arc / E.ASM next >
Encoding:
Assembly Source File  |  1989-06-12  |  48.2 KB  |  1,890 lines

  1. ;Programmer's editor.  Assemble with TASM.  (C) David Nye, 1989.
  2.  
  3. IDEAL
  4. DOSSEG
  5. MODEL TINY
  6.  
  7. MACRO String Name, Text
  8. LOCAL L1
  9. Name    db L1-Name-1, Text
  10. L1:
  11. ENDM
  12.  
  13. ;Constants
  14. LINELENGTH      EQU 80          ;Length of string storage for line
  15. SCREENLENGTH    EQU 24          ;Number of rows in display window
  16. MAXLINES        EQU 2000        ;Size of array of line pointers
  17. BUFFERLENGTH    EQU 1200h       ;Length of file buffer
  18. BELL            EQU 7           ;Some non-printing chars
  19. BS              EQU 8
  20. HT              EQU 9
  21. LF              EQU 10
  22. CR              EQU 13
  23. CRLF            EQU 0A0Dh
  24. CTRL_Z          EQU 26
  25. ESCAPE          EQU 27
  26. DEL             EQU 127
  27. ATTRIB_NL       EQU 2
  28. ATTRIB_INV      EQU 70h
  29.  
  30. DATASEG
  31. ;Strings
  32. String cantOpenMsg,     "Can't open file."
  33. String rdErrorMsg,      'Error reading file.'
  34. String fileErrorMsg,    'File error.'
  35. String noRoomMsg,       'Out of memory.'
  36. String notMarkingMsg,   'Not marking.'
  37. String setLabelMsg,     'Label (0-9): '
  38. String setTabsMsg,      'Tab width: '
  39. String newFileMsg,      'File name: '
  40. String gotoMsg          'Jump to what line? '
  41. String editingMsg       <'Help F1',186,'Editing: '>
  42. String findMsg          'Find: '
  43. String replaceMsg       'Replace with: '
  44. String notFoundMsg      'No more matching strings found.'
  45. String endHelpMsg       'Press any key to continue.'
  46. String ctrlCMsg         '*Break*'
  47. String cancelledMsg     'Cancelled.'
  48. BAK                     db  '.BAK', 0
  49. helpMsg                 db 'left                left arrow, ^s      '
  50.                         db '          mark start of block    @m     '
  51.                         db 'right               right arrow, ^d     '
  52.                         db '          copy block             @c *   '
  53.                         db 'word left           ^left arrow, ^a     '
  54.                         db '          delete block           @d *   '
  55.                         db 'word right          ^right arrow, ^f    '
  56.                         db '          insert block           @i *   '
  57.                         db 'start of line       Home                '
  58.                         db '          empty block buffer     @e *   '
  59.                         db 'end of line         End                 '
  60.                         db '          unmark                 @u     '
  61.                         db 'up                  up arrow, ^e        '
  62.                         db 40 dup ( )
  63.                         db 'down                down arrow, ^x      '
  64.                         db '          find                   @f +   '
  65.                         db 'page up             PgUp, ^r            '
  66.                         db '          replace                @r +   '
  67.                         db 'page down           PgDn, ^c            '
  68.                         db '          find/replace all       @= +   '
  69.                         db 'start of file       ^PgUp               '
  70.                         db 40 dup ( )
  71.                         db 'end of file         ^PgDn               '
  72.                         db '          save and continue      @s     '
  73.                         db 40 dup ( )
  74.                         db '          save and exit          @x     '
  75.                         db 'backspace           Backspace           '
  76.                         db '          kill save on exit      @k     '
  77.                         db 'delete              Del                 '
  78.                         db 40 dup ( )
  79.                         db 'delete word left    ^[                  '
  80.                         db '          label (0-9)            @l     '
  81.                         db 'delete word right   ^], ^t              '
  82.                         db '          go to label (0-9)      @g     '
  83.                         db 'delete to EOL       ^\                  '
  84.                         db '          jump to line #         @j     '
  85.                         db 'delete line         ^-, ^y              '
  86.                         db '          set tab width          @t     '
  87.                         db 'undelete line       ^^                  '
  88.                         db '          toggle autoindent      @a     '
  89.                         db 'toggle insert mode  Ins                 '
  90.                         db '          open another file      @o     '
  91.                         db 80 dup ( )
  92.                         db '@ = Alt, ^ = Ctrl, * = to/from file if s'
  93.                         db 'hifted, + = use last string if shifted. '
  94.                         db 'Status line flags:  Insert  Overwrite  C'
  95.                         db 'hanged  AutoInsert                      '
  96.  
  97.  
  98. ;Variables
  99. newFile?        db ?            ;True if new file
  100. inserting?      db -1           ;True if in insert mode
  101. autoIndent?     db -1           ;True if in autoindent mode
  102. marking?        db 0            ;True if marking text
  103. changed?        db 0            ;True if original file has been changed
  104. isBAKed?        db 0            ;True if .BAK file already written
  105. justFound?      db ?            ;True if no other commands since last Find
  106. needCopies?     db -1           ;True unless lines in buffer were just deleted
  107. autoReplace?    db 0            ;-1 if auto-replace with shift, 1 without shift
  108. row             db ?            ;Current row
  109. column          db ?            ;Current column
  110. tabSize         db 8            ;Tab increment
  111. fHandle         dw ?            ;File handle
  112. lastLine        dw ?            ;Index of last line
  113. top             dw ?            ;Index of first line on screen
  114. bottom          dw ?            ;Index of last line on screen
  115. bufferPtr       dw ?            ;Multipurpose buffer pointer
  116. mark            dw ?            ;Start of marking for block command
  117. here            dw ?            ;Temporary
  118. hereCol         dw ?            ;Temporary
  119. videoSegment    dw ?            ;Segment of system's video memory
  120. fName           db 20 dup (?)   ;File name in ASCIIZ format
  121. fNameBAK        db 20 dup (?)   ;Current file with .BAK extension added
  122. fNameBlock      db 20 dup (?)   ;File to be used in block read/writes
  123. pad             db 20 dup (?)   ;Scratch buffer
  124. heapStart       dw ?            ;Segment of start of heap
  125. heapPtr         dw ?            ;Segment pointer to next free paragraph in heap
  126. labelTable      dw 10 dup (0)   ;Table of line pointers assigned to labels
  127. findString      dw LINELENGTH dup (?)  ;Search string for Find command
  128. replaceString   dw LINELENGTH dup (?)  ;New string for Replace command
  129. linePtrs        dw MAXLINES dup (?) ;List of line pointers
  130. blockPtrs       dw MAXLINES dup (?) ;Line pointers for block commands
  131. blockPtrsLast   dw OFFSET blockPtrs
  132. delLinePtrs     dw MAXLINES dup (?) ;Line pointers for del/undel lines
  133. delLinePtrsLast dw OFFSET delLinePtrs
  134. buffer          db BUFFERLENGTH dup (?)  ;File and delete buffer (dual purpose)
  135. bufferEnd:
  136.  
  137. ;Jump tables:   ^ = Ctrl, @ = Alt, # = Shift.
  138. ctrlTable       dw na           ;^@
  139.                 dw WordLeft     ;^A
  140.                 dw na           ;^B
  141.                 dw PageDown     ;^C
  142.                 dw Right        ;^D
  143.                 dw Up           ;^E
  144.                 dw WordRight    ;^F
  145.                 dw na           ;^G or BEL
  146.                 dw BackSpace    ;^H or BS
  147.                 dw Tab          ;^I or HT
  148.                 dw na           ;^J or LF
  149.                 dw na           ;^K or VT
  150.                 dw na           ;^L or FF
  151.                 dw CRet         ;^M or CR
  152.                 dw na           ;^N or SO
  153.                 dw na           ;^O or SI
  154.                 dw na           ;^P
  155.                 dw na           ;^Q or DC1
  156.                 dw PageUp       ;^R or DC2
  157.                 dw Left         ;^S or DC3
  158.                 dw DeleteWordR  ;^T or DC4
  159.                 dw na           ;^U
  160.                 dw na           ;^V
  161.                 dw na           ;^W
  162.                 dw Down         ;^X or CAN
  163.                 dw DeleteLine   ;^Y
  164.                 dw na           ;^Z
  165.                 dw DeleteWordL  ;^[ or ESC
  166.                 dw DeleteToEOL  ;^\
  167.                 dw DeleteWordR  ;^]
  168.                 dw UndeleteLine ;^^
  169.                 dw DeleteLine   ;^-
  170.  
  171. auxTable        dw 15 DUP (na)  ;Undefined (except for NULL = 3)
  172.                 dw ReverseTab   ;#Tab
  173.                 dw na           ;@Q
  174.                 dw na           ;@W
  175.                 dw EmptyBuffer  ;@E
  176.                 dw Replace      ;@R
  177.                 dw SetTabs      ;@T
  178.                 dw na           ;@Y
  179.                 dw Unmark       ;@U
  180.                 dw InsertBlock  ;@I
  181.                 dw OtherFile    ;@O
  182.                 dw na           ;@P
  183.                 dw 4 DUP (na)   ;Undefined
  184.                 dw AutoIndent   ;@A
  185.                 dw Save         ;@S
  186.                 dw DeleteBlock  ;@D
  187.                 dw Find         ;@F
  188.                 dw GotoLabel    ;@G
  189.                 dw Help         ;@H
  190.                 dw Jump         ;@J
  191.                 dw Kill         ;@K
  192.                 dw SetLabel     ;@L
  193.                 dw 5 DUP (na)   ;Undefined
  194.                 dw na           ;@Z
  195.                 dw Exit         ;@X
  196.                 dw Copy         ;@C
  197.                 dw na           ;@V
  198.                 dw na           ;@B
  199.                 dw na           ;@N
  200.                 dw BeginBlock   ;@M
  201.                 dw 8 DUP (na)   ;Undefined
  202.                 dw Help         ;F1
  203.                 dw na           ;F2
  204.                 dw na           ;F3
  205.                 dw na           ;F4
  206.                 dw na           ;F5
  207.                 dw na           ;F6
  208.                 dw na           ;F7
  209.                 dw na           ;F8
  210.                 dw na           ;F9
  211.                 dw na           ;F10
  212.                 dw 2 DUP (na)   ;Undefined
  213.                 dw HomeLine     ;Home
  214.                 dw Up           ;Up arrow
  215.                 dw PageUp       ;PgUp
  216.                 dw na           ;Undefined
  217.                 dw Left         ;Left arrow
  218.                 dw na           ;Undefined
  219.                 dw Right        ;Right arrow
  220.                 dw na           ;Undefined
  221.                 dw EndLine      ;End
  222.                 dw Down         ;Down arrow
  223.                 dw PageDown     ;PgDn
  224.                 dw ToggleIns    ;Ins
  225.                 dw Delete       ;Del
  226.                 dw 30 DUP (na)  ;[#Fn, ^Fn, @Fn]
  227.                 dw na           ;^PrtSc
  228.                 dw WordLeft     ;^Left arrow
  229.                 dw WordRight    ;^Right arrow
  230.                 dw na           ;^End
  231.                 dw BottomFile   ;^PgDn
  232.                 dw na           ;^Home
  233.                 dw 10 DUP (na)  ;[Alt numbers]
  234.                 dw na           ;@-
  235.                 dw AutoReplace  ;@=
  236.                 dw TopFile      ;^PgUp
  237.  
  238. ;******************************************************************************
  239.  
  240. CODESEG
  241.   mov ax, cs
  242.   mov es, ax
  243.   mov si, 80h                   ;Make pointer to command tail
  244.   mov cl, [si]                  ;Get filename length
  245.   sub ch, ch
  246.   or cl, cl                     ;Forget to include a filename?
  247.   jne @@L0                      ;Get one
  248.   mov ds, ax
  249. @@La:
  250.   mov dx, OFFSET fName
  251.   call GetFileName
  252.   jnz @@Lb
  253.   call Beep
  254.   jmp @@La
  255. @@Lb:
  256.   jmp SHORT InitFile
  257. @@L0:
  258.   mov al, ' '                   ;Skip leading blanks
  259. @@L1:
  260.   inc si
  261.   cmp al, [si]
  262.   loope @@L1
  263.   inc cx
  264.   mov di, OFFSET fName          ;Move command tail to FName
  265.   rep movsb
  266.   sub al, al                    ;Make ASCIIZ string
  267.   stosb
  268.   mov ax, 2523h                 ;Redirect Ctrl C handler
  269.   mov dx, SEG Cancel
  270.   mov ds, dx
  271.   mov dx, OFFSET Cancel
  272.   int 21h
  273.   mov ah, 0Fh                   ;Get display segment
  274.   int 10h
  275.   mov bx, 0B000h                ;B000h for video mode 7 (= MDA or Herc)
  276.   cmp al, 7
  277.   je @@L2
  278.   mov bx, 0B800h                ;B800h for the rest
  279. @@L2:
  280.   mov [cs:videoSegment], bx
  281. InitFile:
  282.   mov ax, cs                    ;Compute starting segment of heap
  283.   mov ds, ax
  284.   add ax, 1000h
  285.   mov [heapStart], ax
  286.   mov dx, OFFSET fName          ;Open file and set up list of line pointers
  287.   call OpenFile
  288.  
  289. NextKey:
  290.   call Redraw                   ;Redraw screen, status line
  291. NextNoRedraw:
  292.   call DrawCursor               ;Place cursor
  293.   sub ah, ah                    ;Get keypress to AL
  294.   int 16h
  295.   or al, al                     ;Check for control codes
  296.   je IsAux
  297.   cmp al, ' '
  298.   jl IsCtrl
  299.   call Insert                   ;Insert or overwrite if none
  300.   jmp NextKey
  301.  
  302. IsAux:
  303.   xchg al, ah                   ;Get aux code
  304.   cmp al, 132
  305.   ja NextKey
  306.   mov si, OFFSET auxTable       ;Jump indirect to appropriate routine
  307. DoTableJump:
  308.   shl ax, 1
  309.   add si, ax
  310.   call [WORD si]
  311.   jmp NextKey
  312.  
  313. IsCtrl:
  314.   mov si, OFFSET ctrlTable      ;Jump to routine through table
  315.   sub ah, ah
  316.   jmp DoTableJump
  317.  
  318. OtherFile:
  319. ;Close current file and open another
  320.   mov dx, OFFSET fName          ;Prompt for new file name
  321.   call GetFileName
  322.   jz Ret0                       ;Abort if null string
  323.   test [newFile?], -1           ;If previous file was old file or was changed,
  324.   jnz @@L1
  325.   test [changed?], -1
  326.   jz @@L2
  327. @@L1:
  328.   call Save                     ;Save file and close it, then open new one ...
  329.   mov bx, [fHandle]
  330.   mov ah, 3Eh
  331.   int 21h
  332. @@L2:
  333.  
  334. OpenFile:
  335. ;Open file, load if found.  Call with dx -> ASCIIZ file name.
  336.   mov [newFile?], 0
  337.   mov [changed?], 0
  338.   mov ax, [heapStart]
  339.   mov [heapPtr], ax
  340.   mov ax, 3D02h                 ;Try to open file
  341.   int 21h
  342.   jnc OldFile
  343.   mov [newFile?], -1            ;If no such file, set newFile? flag
  344.   mov bx, OFFSET linePtrs       ;Set up new file as single blank line
  345.   mov [lastLine], bx
  346.   call NewFile
  347.   jmp XOpenFile
  348. OldFile:
  349.   mov [fHandle], ax             ;Else save file handle
  350.   mov bx, OFFSET linePtrs       ;Read file in
  351.   mov dx, [fHandle]
  352.   call ReadFile
  353.   dec bx
  354.   dec bx
  355.   mov [lastLine], bx            ;Save index of last line
  356. XOpenFile:
  357.   mov bx, OFFSET linePtrs       ;Reset row, screen, buffer pointers
  358.   mov [top], bx
  359.   mov es, [bx]
  360.   sub di, di
  361.   mov ax, OFFSET blockPtrs
  362.   mov [blockPtrsLast], ax
  363. Ret0:
  364.   ret
  365.  
  366. GetFileName:
  367. ;Prompt for file name.  Abort if null name.  Call with buffer address in DX.
  368.   push si
  369.   push dx
  370.   mov si, OFFSET newFileMsg     ;Print prompt
  371.   call Prompt
  372.   pop dx
  373.   call GetString                ;Get file name
  374.   mov si, dx                    ;Convert to ASCIIZ
  375.   add si, ax
  376.   mov [BYTE si], 0
  377. @@Lx:
  378.   pop si
  379. Ret5:
  380.   ret
  381.  
  382. GetString:
  383. ;Get string to [DX], return count (minus CR/LF) in AX.  Abort if null string.
  384.   push bx
  385.   push cx
  386.   push si
  387.   push di
  388.   push es
  389.   push dx
  390.   mov dx, OFFSET pad            ;Get string
  391.   mov ah, 3Fh
  392.   sub bx, bx
  393.   mov cx, 20
  394.   int 21h
  395.   dec ax                        ;Strip CR/LF
  396.   dec ax
  397.   jz @@La                       ;Abort if null string
  398.   mov cx, ax                    ;Copy temporary copy of string to [DX]
  399.   pop dx
  400.   push ax
  401.   mov ax, ds
  402.   mov es, ax
  403.   mov si, OFFSET pad
  404.   mov di, dx
  405.   rep movsb
  406.   pop ax
  407.   pop es
  408.   pop di
  409.   pop si
  410.   pop cx
  411.   pop bx
  412.   ret
  413. @@La:
  414.   mov si, OFFSET cancelledMsg   ;Abort if null string
  415.   jmp Abort
  416.  
  417. ReadFile:
  418. ;Load file with handle in DX into memory, assigning segment pointers from BX
  419.   push es                                         
  420.   mov ax, [heapPtr]                             
  421.   mov es, ax
  422.   sub cx, cx
  423.   sub di, di
  424. FillBuffer:                                       
  425.   push bx                       ; Fill buffer
  426.   push cx
  427.   push dx
  428.   mov bx, dx
  429.   mov ah, 3Fh
  430.   mov cx, BUFFERLENGTH
  431.   mov dx, OFFSET buffer
  432.   int 21h
  433.   jnc @@L1                      ; Check for read error
  434.   jmp ReadError
  435. @@L1:
  436.   pop dx
  437.   pop cx
  438.   pop bx
  439.   mov si, OFFSET buffer         ; Set pointers
  440.   add ax, si
  441.   mov [bufferPtr], ax
  442.   cmp ax, OFFSET buffer         ;Exit if empty buffer
  443.   je EndOfFile
  444. NextLine:
  445.   mov al, [si]                  ;Get next char
  446.   cmp al, CR                    ;If char is CR, end of line
  447.   jne @@L2
  448.   inc si                        ; move past CR
  449.   cmp [byte si], LF             ; and LF if present
  450.   jne @@L1
  451.   inc si
  452. @@L1:
  453.   call EndOfLine                ; pad out line with spaces and save it
  454.   jmp SHORT @@L3
  455. @@L2:
  456.   movsb                         ;Else add char to line
  457.   dec cx
  458. @@L3:
  459.   cmp si, [bufferPtr]           ;Loop until end of buffer
  460.   jb NextLine
  461.   cmp si, OFFSET bufferEnd      ;If buffer less than full, indicates end of file
  462.   jae FillBuffer
  463. EndOfFile:
  464.   or di, di                     ;Finish up present line if anything is on it
  465.   je @@L1
  466.   call EndOfLine
  467. @@L1:
  468.   mov [heapPtr], es             ;Update pointer to start of free heap space
  469.   pop es
  470.   ret
  471.  
  472. EndOfLine:
  473.   add cx, LINELENGTH            ;Pad to end with spaces
  474.   jle @@L1                      ;Truncate lines longer than 80 chars
  475.   mov al, ' '
  476.   rep stosb
  477. @@L1:
  478.   mov [bx], es                  ;Store segment of this line
  479.   mov ax, es                    ;Next line
  480.   add ax, 5
  481.   cmp ax, 0A000h                ;Out of room?
  482.   jae SHORT TooBig
  483.   mov es, ax
  484.   inc bx
  485.   inc bx
  486.   sub di, di
  487.   sub cx, cx
  488. Ret3:
  489.   ret
  490. TooBig:
  491.   mov si, OFFSET noRoomMsg
  492.   jmp Abort
  493.  
  494. Redraw:
  495. ;Redraw screen and status line
  496.   mov [here], bx
  497.   mov [hereCol], di
  498.   push bx
  499.   push di
  500.   push ds
  501.   push es
  502.   push di
  503.   mov es, [videoSegment]        ;Get segment for display
  504.   mov si, OFFSET editingMsg     ;Refresh status line:  "Editing ..."
  505.   call Prompt
  506.   mov di, 34                    ;Tab to column 17
  507.   mov si, OFFSET fName          ; <file name>
  508.   mov ah, ATTRIB_INV
  509. @@S1:
  510.   lodsb
  511.   or al, al
  512.   je @@S2
  513.   stosw
  514.   jmp @@S1
  515. @@S2:
  516.   add di, 6                     ;3 spaces
  517.   mov al, 'L'                   ;"L" <line #>
  518.   stosw
  519.   inc di
  520.   inc di
  521.   mov ax, bx
  522.   sub ax, OFFSET linePtrs
  523.   shr ax, 1
  524.   inc ax
  525.   call PrintInt
  526.   add di, 4                     ;2 spaces
  527.   mov al, 'C'                   ;"C" <column number>
  528.   mov ah, ATTRIB_INV
  529.   stosw
  530.   inc di
  531.   inc di
  532.   pop ax                        ;Get copy of DI as cursor row
  533.   inc ax
  534.   call PrintInt
  535.   mov di, 152                   ;Tab to column 76
  536.   mov al, 'I'                   ;Insert/Overwrite status
  537.   mov ah, ATTRIB_INV
  538.   test [inserting?], -1
  539.   jne @@S3
  540.   mov al, 'O'
  541. @@S3:
  542.   stosw
  543.   mov al, 'C'                   ;Changed status
  544.   test [changed?], -1
  545.   jne @@S5
  546.   mov al, ' '
  547. @@S5:
  548.   stosw
  549.   mov al, 'A'
  550.   test [autoIndent?], -1
  551.   jne @@S6
  552.   mov al, ' '
  553. @@S6:
  554.   stosw
  555.   mov di, 160                   ;Move to next display line
  556.   mov ax, [top]                 ;Compute bottom of screen
  557.   mov bx, ax
  558.   add ax, (SCREENLENGTH - 1) * 2
  559.   cmp ax, [lastLine]            ;If at end of file,
  560.   jle @@L0
  561.   mov ax, [lastLine]            ; stop at lastLine
  562. @@L0:
  563.   mov [bottom], ax
  564. @@L1:                           ;For each row
  565.   mov cx, 80                    ;Count of chars per row
  566.   mov ds, [cs:bx]               ;Get pointer to screen line
  567.   sub si, si                    ;Initialize column counter
  568.   mov ah, ATTRIB_NL             ;Attribute = inverse video if Marked
  569.   test [cs:marking?], -1
  570.   je @@L2
  571.   cmp bx, [cs:mark]
  572.   je @@L1b
  573.   jb @@L1a
  574.   cmp bx, [cs:here]
  575.   jbe @@L1b
  576.   jmp SHORT @@L2
  577. @@L1a:
  578.   cmp bx, [cs:here]
  579.   jb @@L2
  580. @@L1b:
  581.   mov ah, ATTRIB_INV
  582. @@L2:                           ;For each char, write char and attribute
  583.   lodsb
  584.   stosw
  585.   loop @@L2                     ;Next char
  586.   inc bx                        ;Next row
  587.   inc bx
  588.   cmp bx, [cs:bottom]           ;Stop if screen full
  589.   jle @@L1
  590.   mov cx, 160*(SCREENLENGTH+1)  ;Fill out screen with blanks
  591.   sub cx, di
  592.   shr cx, 1
  593.   mov ah, ATTRIB_NL
  594.   mov al, ' '
  595.   rep stosw
  596. @@L3:
  597.   pop es
  598.   pop ds
  599.   pop di
  600.   pop bx
  601.   ret
  602.  
  603. DrawCursor:
  604. ;Set cursor shape and place it on screen
  605.   push bx
  606.   mov cl, 13                    ;Set cursor shape depending on Inserting?
  607.   mov ch, 12                    ;Line for insert mode,
  608.   test [Inserting?], -1
  609.   jne @@L1
  610.   sub ch, ch                    ;Block for overwrite
  611. @@L1:
  612.   mov ah, 1
  613.   int 10h
  614.   sub bx, [top]                 ;Show cursor at current row, column
  615.   shr bx, 1
  616.   inc bx
  617.   mov dh, bl
  618.   mov ax, di
  619.   mov dl, al
  620.   mov ah, 2
  621.   mov bh, 0
  622.   int 10h
  623.   pop bx
  624.   ret
  625.  
  626. Print0:
  627.   call ClearStatus              ;Blank status line
  628.   sub di, di                    ;Starting at beginning of line ...
  629.  
  630. Print:
  631. ;Print string pointed to by SI on status line in inverse video, starting at DI
  632.   push es
  633.   mov es, [videoSegment]
  634.   lodsb                         ;Get count of string to be printed
  635.   mov cl, al
  636.   sub ch, ch
  637.   mov ah, ATTRIB_INV            ;Attribute = inverse video
  638. @@L1:
  639.   lodsb
  640.   stosw
  641.   loop @@L1
  642.   pop es
  643.   ret
  644.  
  645. ClearStatus:
  646. ;Inverse-blank status line
  647.   push di
  648.   push es
  649.   mov es, [videoSegment]
  650.   mov ah, ATTRIB_INV
  651.   mov al, ' '
  652.   mov cx, 80
  653.   sub di, di
  654.   rep stosw
  655.   pop es
  656.   pop di
  657.   ret
  658.  
  659. Cancel:
  660. ;Ctrl C routine
  661.   mov si, OFFSET ctrlCMsg       ;Abort with message ...
  662.  
  663. Abort:
  664. ;Print counted string pointed to by SI on status line and abort
  665.   call Print0                   ;Print error message ...
  666.  
  667. na:
  668. ;Unassigned key or other error.  Beep and abort.
  669.   call Beep                     ;Beep
  670.   mov ax, -2                    ;Reset stack pointer to top
  671.   mov sp, ax
  672.   mov bx, [here]                ;Retrieve cursor position in case it was trashed
  673.   mov di, [hereCol]
  674.   call DrawCursor
  675.   jmp NextNoRedraw              ;Restart main editing loop
  676.  
  677. Beep:
  678.   mov ah, 2                     ;Output a BELL
  679.   mov dl, BELL
  680.   int 21h
  681.   ret
  682.  
  683. Prompt:
  684.   push di
  685.   call Print0                   ;Print string at start of line
  686.   mov dx, di                    ;Set cursor to end of printed string ...
  687.   shr dl, 1
  688.   sub dh, dh
  689.   pop di
  690.  
  691. GotoXY:
  692. ;Position cursor at row, column given by DL, DH
  693.   push bx
  694.   mov ah, 2
  695.   sub bx, bx
  696.   int 10h
  697.   pop bx
  698.   ret
  699.  
  700. PrintInt:
  701. ;Print to ES:DI in inverse video the unsigned decimal integer in AX
  702.   sub dx, dx                    ;Start stack with a null
  703.   push dx
  704.   or ax, ax                     ;If integer = 0,
  705.   jne @@L0
  706.   push dx                       ; push another 0 and skip divisions
  707.   jmp SHORT @@L2
  708. @@L0:
  709.   mov cx, 10                    ;Get remainders of successive divisions by 10
  710. @@L1:
  711.   div cx
  712.   add dl, '0'                   ;Convert to ASCII
  713.   mov dh, ATTRIB_INV            ;Attribute is reverse video
  714.   push dx
  715.   sub dx, dx
  716.   or ax, ax
  717.   jne @@L1
  718. @@L2:
  719.   pop ax                        ;Pop and print remainders in reversed order
  720. @@L3:
  721.   stosw
  722.   pop ax
  723.   or ax, ax
  724.   jne @@L3
  725.   ret
  726.  
  727. NewLine:
  728. ;Jump here if cursor row is changed by a command
  729.   mov [justFound?], 0
  730. NewLine0:
  731.   cmp bx, OFFSET linePtrs       ;Check bounds, adjust if necessary
  732.   jge @@L1
  733.   mov bx, OFFSET linePtrs
  734. @@L1:
  735.   cmp bx, [lastLine]
  736.   jle @@L2
  737.   mov bx, [lastLine]
  738. @@L2:
  739.   mov ax, [top]    
  740.   cmp bx, ax
  741.   jge @@L3
  742.   mov [top], bx
  743. @@L3:
  744.   add ax, (SCREENLENGTH-1)*2
  745.   cmp bx, ax
  746.   jle @@L4
  747.   mov ax, bx
  748.   sub ax, (SCREENLENGTH-1)*2
  749.   mov [top], ax
  750. @@L4:
  751.   mov es, [bx]                  ;Adjust ES to point to new line
  752.   ret
  753.  
  754. Left:
  755.   or di, di                     ;If at start of line,
  756.   jne @@L1
  757.   call Up                       ; move to end of line above
  758.   jmp EndLine
  759. @@L1:
  760.   dec di                        ; else just decrement cursor
  761. CursorMoved:
  762.   mov [justFound?], 0
  763.   ret
  764.  
  765. Right:
  766.   cmp di, LINELENGTH - 1        ;If at end of line,
  767.   jne @@L1
  768.   sub di, di                    ; move to start of line below
  769.   jmp Down
  770. @@L1:
  771.   inc di                        ; else just increment cursor
  772.   jmp SHORT CursorMoved
  773.  
  774. LScanE:
  775. ;Scan left past first non-space or start of line
  776.   mov al, ' '      
  777.   mov cx, di
  778.   inc cx
  779.   std
  780.   repe scasb
  781.   cld
  782.   ret
  783.  
  784. LScanNE:
  785. ;Scan left past first space or start of line
  786.   mov al, ' '
  787.   mov cx, di
  788.   inc cx
  789.   std
  790.   repne scasb
  791.   cld
  792.   ret
  793.  
  794. RScanE:
  795. ;Scan right past first non-space or end of line
  796.   mov al, ' '
  797.   mov cx, LINELENGTH
  798.   sub cx, di
  799.   repe scasb
  800.   ret
  801.  
  802. RScanNE:
  803. ;Scan right past first space or end of line
  804.   mov al, ' '
  805.   mov cx, LINELENGTH
  806.   sub cx, di
  807.   repne scasb
  808.   ret
  809.  
  810. WordLeft:
  811. ;Move left one word
  812.   or di, di                     ;Do nothing if at start of line
  813.   je @@Lx
  814.   mov [justFound?], 0
  815.   dec di                        ;Else starting at char to left,
  816.   call LScanE                   ; skip spaces until non-space
  817.   inc di
  818.   je @@Lx                       ; or start of line,
  819.   call LScanNE                  ; then scan to next space or start of line
  820.   jne @@L1
  821.   inc di
  822. @@L1:
  823.   inc di
  824. @@Lx:
  825.   ret
  826.  
  827. WordRight:
  828. ;Move right one word
  829.   cmp di, LINELENGTH - 1        ;Do nothing if at end of line
  830.   je @@Lx
  831.   mov [justFound?], 0
  832.   call RScanNE                  ;Skip non-spaces until space
  833.   jne @@L1                      ; or end of line,
  834.   dec di
  835.   call RScanE                   ; then scan to next non-space or end of line
  836. @@L1:
  837.   dec di
  838. @@Lx:
  839.   ret
  840.  
  841. HomeLine:
  842. ;Move cursor to column zero
  843.   sub di, di
  844.   mov [justFound?], 0
  845.   ret
  846.  
  847. EndLine:
  848. ;Move cursor to end of line
  849.   push cx
  850.   mov [justFound?], 0
  851.   mov di, LINELENGTH - 1        ;Start at end of line
  852.   call LScanE                   ;Skip all spaces until non-space
  853.   je @@L1                       ; or beginning of line
  854.   inc di
  855.   cmp di, LINELENGTH - 1
  856.   je @@L2
  857. @@L1:
  858.   inc di
  859. @@L2:
  860.   pop cx
  861. Ret2:
  862.   ret
  863.  
  864. Up:
  865. ;Move cursor up one line
  866.   cmp bx, OFFSET linePtrs       ;If at top of file already, do nothing
  867.   je Ret2
  868.   dec bx
  869.   dec bx
  870.   jmp NewLine
  871.  
  872. Down:
  873. ;Move cursor down one line
  874.   cmp bx, [lastLine]            ;If at last line already, do nothing
  875.   je Ret2
  876.   inc bx
  877.   inc bx
  878.   jmp NewLine
  879.  
  880. PageUp:
  881. ;Move cursor up one page
  882.   sub bx, (SCREENLENGTH-1)*2
  883.   jmp NewLine
  884.  
  885. PageDown:
  886. ;Move cursor down one page
  887.   add bx, (SCREENLENGTH-1)*2
  888.   jmp NewLine
  889.  
  890. TopFile:
  891. ;Move cursor to top of file
  892.   mov bx, OFFSET linePtrs
  893.   mov [top], bx
  894.   call HomeLine
  895.   jmp NewLine
  896.  
  897. BottomFile:
  898. ;Move cursor to bottom of file
  899.   mov bx, [lastLine]
  900.   mov es, [bx]
  901.   mov ax, bx
  902.   sub ax, (SCREENLENGTH-1)*2
  903.   cmp ax, OFFSET linePtrs
  904.   ja @@L1
  905.   mov ax, OFFSET linePtrs
  906. @@L1:
  907.   mov [top], ax
  908.   call EndLine
  909.   jmp NewLine
  910.  
  911. Tab:
  912. ;Tab right
  913.   mov [justFound?], 0
  914.   mov ax, di                    ;Advance di to next tab stop
  915.   mov cl, [tabSize]
  916.   div cl
  917.   sub cl, ah
  918.   sub ch, ch
  919.   add di, cx
  920.   cmp di, LINELENGTH            ;If past end of line,
  921.   jl @@L1
  922.   mov di, LINELENGTH - 1        ;Set cursor at end of line
  923. @@L1:
  924.   ret
  925.  
  926. ReverseTab:
  927. ;Tab left
  928.   mov [justFound?], 0
  929.   mov ax, di                    ;Decrement di to nearest tab stop
  930.   dec al
  931.   div [tabSize]
  932.   mov al, ah
  933.   sub ah, ah
  934.   inc al
  935.   sub di, ax
  936.   jnc @@L1                      ;Set to start of line if past start
  937.   sub di, di
  938. @@L1:
  939.   ret
  940.  
  941. CRet:
  942. ;Split line at cursor
  943.   push ds
  944.   push es
  945.   push di
  946.   push es
  947.   call InsertLine               ;Start a new line below current one, ->ES:DI
  948.   pop ds                        ;DS:SI := current cursor position
  949.   pop si
  950.   push di
  951.   mov cx, LINELENGTH            ;CX := # chars left on line
  952.   sub cx, si
  953.   je @@L2
  954. @@L1:
  955.   movsb                         ;Split line,
  956.   mov [byte si - 1], ' '        ; blank original to end from cursor
  957.   loop @@L1
  958. @@L2:
  959.   pop di
  960.   pop es
  961.   pop ds
  962.   mov [changed?], -1            ;Mark file as changed
  963.   jmp NewLine
  964.  
  965. InsertLine:
  966. ;Insert a new blank line below current one
  967.   mov cx, 1                     ;Make room for new entry in linePtr
  968.   call OpenRow
  969.   inc bx
  970.   inc bx
  971.   mov ax, [heapPtr]
  972.   jmp SHORT BlankLine
  973.  
  974. NewFile:
  975. ;Set up initial blank line of a new file
  976.   mov ax, [heapStart]           ;Set ES and [bx] to available heap
  977. BlankLine:
  978.   mov [bx], ax
  979.   mov es, ax
  980.   add ax, 5
  981.   mov [heapPtr], ax             ;Update heap pointer (segment value only)
  982.   sub di, di                    ;Blank new line
  983.   mov cx, LINELENGTH
  984.   mov al, ' '
  985.   rep stosb
  986.   sub di, di                    ;Home cursor on new line
  987.   test [autoIndent?], -1        ; or if in autoindent mode,
  988.   je @@Lx
  989.   cmp bx, OFFSET linePtrs       ; and this is not first line in file,
  990.   je @@Lx
  991.   mov es, [bx - 2]              ; line up with first char of line above
  992.   call RScanE
  993.   mov es, [bx]
  994.   dec di
  995.   cmp di, LINELENGTH - 1        ; unless above line is blank
  996.   jb @@Lx
  997.   sub di, di
  998. @@Lx:
  999.   ret
  1000.  
  1001. OpenRow:
  1002. ;Open CX lines at BX in linePtrs
  1003.   push cx
  1004.   push di
  1005.   push es
  1006.   mov ax, ds                    ;DS, ES -> data segment (for linePtr)
  1007.   mov es, ax
  1008.   mov si, [lastLine]            ;SI points to last line's segment pointer
  1009.   mov di, si                    ;DI points CX lines beyond that
  1010.   add di, cx
  1011.   add di, cx
  1012.   mov [lastLine], di            ;Point lastLine to new last line
  1013.   mov cx, si                    ;Count = # lines from here to end
  1014.   sub cx, bx
  1015.   shr cx, 1
  1016.   inc cx
  1017.   std                                                                
  1018.   rep movsw                     ;Move array elements up
  1019.   cld
  1020.   pop es
  1021.   pop di
  1022.   pop cx
  1023.   ret
  1024.  
  1025. Backspace:
  1026. ;Delete char to left of cursor
  1027.   mov ax, di                    ;Unless at first character of file,
  1028.   add ax, bx
  1029.   sub ax, OFFSET linePtrs
  1030.   jz Ret1                       ; do Left then Delete
  1031.   mov [justFound?], 0
  1032.   push di
  1033.   call Left
  1034.   pop ax                        ;Don't do Join if already at end of line ...
  1035.   or ax, ax
  1036.   jne Delete0
  1037.  
  1038. Delete:
  1039. ;Delete char at cursor
  1040.   mov dx, di                    ;Save cursor column
  1041.   cmp [byte ptr es:LINELENGTH-1], ' ' ;If deleting a space at end of line,
  1042.   jne Delete0
  1043.   call EndLine
  1044.   xchg di, dx
  1045.   cmp di, dx
  1046.   jge Join                      ; join to line below
  1047. Delete0:
  1048.   push di                       ; else slide text left
  1049.   push cx
  1050.   push ds
  1051.   mov cx, LINELENGTH - 1
  1052.   sub cx, di
  1053.   mov si, di
  1054.   inc si
  1055.   mov ax, es
  1056.   mov ds, ax
  1057.   rep movsb
  1058.   mov [BYTE di], ' '            ;Blank last character on line
  1059.   pop ds
  1060.   pop cx
  1061.   pop di
  1062.   mov [changed?], -1
  1063. Ret1:
  1064.   ret
  1065.  
  1066. UndeleteLine:
  1067.   mov bp, [delLinePtrsLast]     ;Abort if no lines are in buffer
  1068.   cmp bp, OFFSET delLinePtrs
  1069.   ja @@L0
  1070.   jmp Beep
  1071. @@L0:
  1072.   dec bp                        ;Else move pointer to top line of delete buffer
  1073.   dec bp
  1074.   mov [delLinePtrsLast], bp
  1075.   or di, di                     ;If cursor is at start of line,
  1076.   jne @@L1
  1077.   mov cx, 1
  1078.   call OpenRow                  ;Start new row below current one
  1079.   mov [bx+2], es                ;Swap rows to insert undeleted above current
  1080.   mov ax, [bp]                  ;Retrieve and store pointer to undeleted line
  1081.   mov [bx], ax
  1082.   jmp NewLine
  1083. @@L1:
  1084.   mov cx, LINELENGTH            ;Cursor not at start of line
  1085.   sub cx, di                    ;Copy popped line over current one
  1086.   push di
  1087.   push ds
  1088.   mov ds, [bp]
  1089.   sub si, si
  1090.   rep movsb
  1091.   pop ds
  1092.   pop di
  1093.   ret
  1094.  
  1095. Join:
  1096. ;Join lower line to current line at cursor
  1097.   cmp bx, [lastLine]            ;Abort if this is the last line of the file
  1098.   je @@Lx
  1099.   push di                       ;Save registers
  1100.   push ds
  1101.   push di
  1102.   push es
  1103.   mov es, [bx + 2]              ;Get next line's segment
  1104.   push es                       ;Save a copy
  1105.   mov dx, di                    ;Get length of lower line:
  1106.   call EndLine                  ;Find first non-space char from end
  1107.   add dx, di                    ;If concatenated line is too long, abort.
  1108.   cmp dx, LINELENGTH
  1109.   jbe @@L0
  1110.   call Beep
  1111.   pop ax
  1112.   pop es
  1113.   pop di
  1114.   pop ds
  1115.   pop ax
  1116.   jmp Ret1
  1117. @@L0:
  1118.   mov cx, di                    ;Count = lower line length
  1119.   sub si, si                    ;Source = start of lower line
  1120.   pop ds
  1121.   pop es                        ;Destination = present cursor location
  1122.   pop di
  1123.   rep movsb                     ;Concatenate lines
  1124.   pop ds
  1125.   inc bx                        ;Delete lower line
  1126.   inc bx
  1127.   call DeleteLineNS
  1128.   cmp bx, [lastLine]
  1129.   je @@L1
  1130.   dec bx
  1131.   dec bx
  1132. @@L1:
  1133.   pop di                        ;Restore pointers and return
  1134. @@Lx:
  1135.   jmp NewLine
  1136.  
  1137. Insert:
  1138. ;Insert or overwrite at cursor
  1139.   mov [justFound?], 0
  1140.   test [inserting?], -1         ;If inserting, open up space for new character
  1141.   jz Insert1
  1142.   cmp [BYTE es:LINELENGTH - 1], ' ' ; unless line is full (then beep)
  1143.   je Insert0
  1144.   jmp Beep
  1145. Insert0:
  1146.   push ax
  1147.   push cx
  1148.   push ds
  1149.   mov ax, es
  1150.   mov ds, ax
  1151.   mov si, LINELENGTH - 1
  1152.   mov cx, si
  1153.   sub cx, di
  1154.   mov di, si
  1155.   dec si
  1156.   std
  1157.   rep movsb
  1158.   cld
  1159.   pop ds
  1160.   pop cx
  1161.   pop ax
  1162. Insert1:
  1163.   stosb                         ;Add character
  1164.   mov [changed?], -1
  1165.   cmp di, LINELENGTH            ;Don't advance DI if at end of line
  1166.   jb @@L1
  1167.   dec di
  1168. @@L1:
  1169.   ret
  1170.  
  1171. DeleteToEOL:
  1172. ;Delete from cursor to end of line
  1173.   mov [justFound?], 0
  1174.   push bx                       ;Save regs to return to current cursor position
  1175.   push di
  1176.   push es
  1177.   push [word autoIndent?]       ;Turn autoIndent off
  1178.   mov [autoIndent?], 0
  1179.   call Cret                     ;Do Enter then delete lower line
  1180.   call DeleteLine
  1181.   pop [word autoIndent?]
  1182.   pop es
  1183.   pop di
  1184.   pop bx
  1185.   jmp NewLine
  1186.  
  1187. DeleteLine:
  1188. ;Delete cursor line and append to buffer
  1189.   mov bp, [delLinePtrsLast]     ;Save segment of current line in delete buffer
  1190.   mov [bp], es
  1191.   inc bp
  1192.   inc bp
  1193.   mov [delLinePtrsLast], bp
  1194. DeleteLineNS:                   ;Enter here if we don't want to save line
  1195.   mov di, bx                    ;Delete line:  destination = this line
  1196.   mov si, di                    ;Source = next line
  1197.   inc si
  1198.   inc si
  1199.   mov cx, [lastLine]            ;Count = number of lines from here to end
  1200.   mov ax, cx
  1201.   dec ax
  1202.   dec ax
  1203.   mov [lastLine], ax
  1204.   sub cx, bx
  1205.   shr cx, 1
  1206.   mov ax, ds                    ;Move line segment values above cursor down
  1207.   mov es, ax     
  1208.   rep movsw
  1209.   mov [changed?], -1
  1210.   sub di, di                    ;Home cursor on new line
  1211.   jmp NewLine
  1212.  
  1213. DeleteWordL:
  1214. ;Delete left to space or column zero
  1215.   mov si, di                    ;Save cursor column
  1216.   call WordLeft                 ;Tab left one word
  1217.   push di
  1218. CloseGap:
  1219.   mov cx, LINELENGTH            ;Close gap between di and si cursor positions
  1220.   sub cx, si
  1221.   push ds
  1222.   mov ax, es
  1223.   mov ds, ax
  1224.   rep movsb
  1225.   mov cx, LINELENGTH            ;Pad end of line with spaces
  1226.   sub cx, di
  1227.   mov al, ' '
  1228.   rep stosb
  1229.   mov [changed?], -1
  1230.   pop ds
  1231.   pop di
  1232.   ret
  1233.  
  1234. DeleteWordR:
  1235. ;Delete right to space or end of line
  1236.   mov si, di                    ;Save cursor
  1237.   push di
  1238.   call WordRight                ;Tab right one word
  1239.   xchg si, di                   ;Close up space between si and di
  1240.   jmp CloseGap
  1241.  
  1242. ToggleIns:
  1243.   not [inserting?]
  1244.   ret
  1245.  
  1246. Jump:
  1247. ;Jump to line number n
  1248.   mov si, OFFSET gotoMsg
  1249.   call Prompt
  1250.   call GetInt
  1251.   dec ax
  1252.   shl ax, 1
  1253.   mov bx, ax
  1254.   add bx, OFFSET linePtrs
  1255.   mov [justFound?], 0
  1256.   jmp JL1                       ;Jump to address
  1257.  
  1258. GetInt:
  1259. ;Get a decimal integer from keyboard to AX.  Carry set on improper input.
  1260. ;Abort if null input.
  1261.   push bx
  1262.   push cx
  1263.   push dx
  1264.   push si
  1265.   mov dx, OFFSET buffer
  1266.   call GetString                ;Input a string
  1267.   mov cx, ax                    ;Construct integer a digit at a time
  1268.   mov si, OFFSET buffer
  1269.   sub ax, ax
  1270.   mov bh, 10
  1271. @@L1:
  1272.   mov bl, [si]                  ;Get next char
  1273.   inc si
  1274.   sub bl, '0'
  1275.   jc @@Lx                       ;Exit with carry set if not a digit
  1276.   cmp bl, '9'
  1277.   cmc
  1278.   jc @@Lx
  1279.   mul bh                        ;AX := AX + (new digit - '0')
  1280.   add al, bl
  1281.   adc ah, 0
  1282.   jc @@Lx                       ;Check for overflow
  1283.   loop @@L1                     ;Next char
  1284. @@Lx:
  1285.   pop si                        ;Return with int in AX, carry set if error
  1286.   pop dx
  1287.   pop cx
  1288.   pop bx
  1289.   ret
  1290. @@La:
  1291.   mov si, OFFSET cancelledMsg
  1292.   jmp Abort
  1293.  
  1294. SetLabel:
  1295. ;Set label 0-9 at current line
  1296.   call GetLabel
  1297.   mov [si], bx
  1298.   ret
  1299.  
  1300. GotoLabel:
  1301. ;Goto label 0-9 previously set by SetLabel
  1302.   call GetLabel
  1303.   cmp [WORD si], 0
  1304.   je JLx
  1305.   mov bx, [si]                  ;Retrieve address
  1306.   mov [justFound?], 0
  1307. JL1:
  1308.   mov ax, bx
  1309.   sub ax, 8                     ;Make cursor line fifth from top
  1310.   cmp ax, OFFSET linePtrs
  1311.   jge @@L1
  1312.   mov ax, OFFSET linePtrs
  1313. @@L1:
  1314.   mov [top], ax
  1315. JLx:
  1316.   jmp NewLine0
  1317.  
  1318. GetLabel:
  1319.   mov si, OFFSET setLabelMsg
  1320.   call Prompt
  1321.   mov ah, 8                     ;Get char from keyboard
  1322.   int 21h
  1323.   mov dl, al                    ;Save copy to echo
  1324.   sub al, '0'                   ;Don't accept input if not a digit
  1325.   jl GetLabel
  1326.   cmp al, 9
  1327.   jg GetLabel
  1328.   mov ah, 2
  1329.   mov cl, al
  1330.   int 21h
  1331.   mov si, OFFSET LabelTable     ;Form index into LabelTable
  1332.   shl cl, 1
  1333.   sub ch, ch
  1334.   add si, cx                    ;Return address of label storage in SI
  1335.   ret
  1336.  
  1337. SetTabs:
  1338. ;Set tab width
  1339.   mov si, OFFSET setTabsMsg
  1340.   call Prompt
  1341.   call GetInt
  1342.   mov [tabSize], al
  1343.   ret
  1344.  
  1345. AutoIndent:
  1346. ;Toggle autoindent mode
  1347.   not [autoIndent?]
  1348.   ret
  1349.  
  1350. Kill:
  1351. ;Clear changed? flag so file changes will be discarded on exit
  1352.   mov [changed?], 0
  1353.   ret
  1354.  
  1355. Save:
  1356. ;Write lines to file, renaming old version with .BAK extension
  1357.   push dx
  1358.   push di
  1359.   push es
  1360.   push bx
  1361.   mov al, [changed?]            ;If no changes, done.
  1362.   or al, al
  1363.   jnz @@L0
  1364.   jmp XSave
  1365. @@L0:
  1366.   mov al, [newFile?]            ;If a new file, create it first
  1367.   or al, [isBAKed?]             ;If already BAKed up, no .BAK needed
  1368.   jnz DoSave
  1369.   mov ah, 3Eh                   ;Else close file
  1370.   mov bx, [fHandle]
  1371.   int 21h
  1372.   mov ax, ds
  1373.   mov es, ax
  1374.   mov si, OFFSET fName          ;Make new ASCIIZ string with .BAK extension
  1375.   mov di, OFFSET fNameBAK
  1376. @@L1:
  1377.   lodsb
  1378.   cmp al, '.'
  1379.   je @@L2
  1380.   or al, al
  1381.   je @@L2
  1382.   stosb
  1383.   jmp SHORT @@L1
  1384. @@L2:
  1385.   mov cx, 4
  1386.   mov si, OFFSET BAK
  1387.   rep movsb
  1388.   mov ah, 41h                   ;Delete old back-up copy
  1389.   mov dx, OFFSET fNameBAK
  1390.   int 21h
  1391.   mov dx, OFFSET fName          ;Rename current file to file.BAK
  1392.   mov di, OFFSET fNameBAK
  1393.   mov ah, 56h
  1394.   int 21h
  1395. DoSave:
  1396.   mov ah, 3Ch                   ;CREATe new file with old name
  1397.   sub cx, cx
  1398.   mov dx, OFFSET fName
  1399.   int 21h
  1400.   jc CantOpen
  1401.   mov [fHandle], ax
  1402.   mov [isBAKed?], -1            ;Set flag so we only make .BAK file once
  1403.   mov bx, OFFSET linePtrs       ;Write file
  1404.   call WriteFile
  1405. XSave:
  1406.   pop bx
  1407.   pop es
  1408.   pop di
  1409.   pop dx
  1410.   ret
  1411.  
  1412. WriteFile:
  1413. ;Write lines out to file starting at BX and ending at [lastLine]
  1414.   push es
  1415.   push di
  1416.   mov di, OFFSET buffer
  1417. @@L1:
  1418.   mov si, di                    ;Preserve file buffer pointer
  1419.   mov es, [bx]                  ;Strip trailing blanks
  1420.   mov cx, LINELENGTH
  1421.   mov di, LINELENGTH - 1
  1422.   mov al, ' '
  1423.   std
  1424.   repe scasb
  1425.   cld
  1426.   je @@L1a
  1427.   inc cx
  1428. @@L1a:
  1429.   mov ax, ds                    ;Copy line to file buffer
  1430.   mov dx, es
  1431.   mov es, ax
  1432.   mov ds, dx
  1433.   mov di, si
  1434.   sub si, si
  1435.   rep movsb
  1436.   mov ax, CRLF                  ;Stick a CRLF on the end
  1437.   stosw
  1438.   mov ax, ds
  1439.   mov dx, es
  1440.   mov es, ax
  1441.   mov ds, dx
  1442.   cmp di, OFFSET Buffer + BUFFERLENGTH - 80  ;If buffer is almost full,
  1443.   jl @@L2
  1444.   call WriteBuffer              ; write it
  1445. @@L2:
  1446.   inc bx                        ;Next line, loop until all lines are written
  1447.   inc bx
  1448.   cmp bx, [lastLine]
  1449.   jle @@L1
  1450.   call WriteBuffer              ;Write final partial buffer to file and exit
  1451.   pop di
  1452.   pop es
  1453.   ret
  1454.  
  1455. FileError:
  1456.   mov si, OFFSET fileErrorMsg
  1457.   jmp Abort
  1458. CantOpen:
  1459.   mov si, OFFSET cantOpenMsg
  1460.   jmp Abort
  1461. NoRoom:
  1462.   mov si, OFFSET noRoomMsg
  1463.   jmp Abort
  1464. ReadError:
  1465.   mov si, OFFSET rdErrorMsg
  1466.   jmp Abort
  1467.  
  1468. WriteBuffer:
  1469. ;Write text in buffer to disk
  1470.   push bx
  1471.   mov ah, 40h
  1472.   mov bx, [fHandle]
  1473.   mov cx, di
  1474.   mov dx, OFFSET Buffer
  1475.   sub cx, dx
  1476.   jz @@L1
  1477.   int 21h
  1478.   jc FileError
  1479.   mov di, OFFSET Buffer
  1480. @@L1:
  1481.   pop bx
  1482.   ret
  1483.  
  1484. Exit:
  1485.   call Save                     ;Save file if changed
  1486.   mov cx, 0C0Dh                 ;Restore standard cursor size
  1487.   mov ah, 1
  1488.   int 10h
  1489.   mov dx, 1800h                 ;Put cursor at bottom of screen
  1490.   sub bh, bh
  1491.   mov ah, 2
  1492.   int 10h
  1493.   mov ax, 4C00h                 ;Bye!
  1494.   int 21h
  1495.  
  1496. BeginBlock:
  1497. ;Start marking block for block operation
  1498.   mov [marking?], -1
  1499.   mov [mark], bx
  1500.   ret
  1501.  
  1502. Unmark:
  1503. ;Clear marking
  1504.   mov [marking?], 0
  1505.   ret
  1506.  
  1507. InsertBlock:
  1508. ;Insert from buffer or named file
  1509.   push bx
  1510.   call FileOrBuffer?            ;From file or buffer?
  1511.   je InsertBuffer
  1512.   mov dx, OFFSET fNameBlock     ;If file, open it
  1513.   mov ax, 3D00h
  1514.   int 21h
  1515.   jnc @@L1
  1516.   jmp CantOpen
  1517. @@L1:
  1518.   mov dx, ax                    ;Load file
  1519.   mov bx, OFFSET blockPtrs
  1520.   mov di, bx
  1521.   call ReadFile
  1522.   mov cx, bx
  1523.   mov bx, dx                    ;Close file
  1524.   mov ah, 3Eh
  1525.   int 21h
  1526.   jmp SHORT DoInsert
  1527. InsertBuffer:                   ;Insert from buffer
  1528.   mov bp, [blockPtrsLast]       ;Abort if empty
  1529.   cmp bp, OFFSET blockPtrs
  1530.   jne @@L0
  1531.   pop bx
  1532.   jmp na
  1533. @@L0:
  1534.   test [needCopies?], -1        ;If not just moving lines, need to duplicate
  1535.   jz @@L2
  1536.   push bx
  1537.   push es
  1538.   push ds
  1539.   mov bx, bp
  1540.   mov dx, [heapPtr]
  1541. @@L1:
  1542.   dec bx                        ;Copy contents of buffered lines to new ones
  1543.   dec bx
  1544.   mov ds, [cs:bx]
  1545.   sub si, si
  1546.   mov es, dx
  1547.   sub di, di
  1548.   mov [cs:bx], es
  1549.   mov cx, LINELENGTH
  1550.   rep movsb
  1551.   add dx, 5
  1552.   cmp dx, 0A000h
  1553.   jb @@L1a
  1554.   jmp TooBig
  1555. @@L1a:
  1556.   cmp bx, OFFSET blockPtrs
  1557.   ja @@L1
  1558.   pop ds
  1559.   pop es
  1560.   pop bx
  1561.   mov [heapPtr], dx
  1562. @@L2:
  1563.   mov [needCopies?], -1
  1564.   mov cx, bp
  1565. DoInsert:
  1566.   pop bx
  1567.   sub cx, OFFSET blockPtrs      ;Get count of lines to move
  1568.   shr cx, 1
  1569.   call OpenRow                  ;Open that much room in array of seg pointers
  1570.   mov si, OFFSET blockPtrs      ;Copy new lines into opening
  1571.   mov di, bx
  1572.   mov ax, ds
  1573.   mov es, ax
  1574.   rep movsw
  1575.   mov [changed?], -1
  1576.   sub di, di
  1577.   jmp NewLine
  1578.  
  1579. FileOrBuffer?:
  1580. ;Prompt for file name if Shift is down, put it in fNameBlock.
  1581. ;If return with Z set, Shift was up, indicating buffer is to be used.
  1582.   call Shifted?                 ;If shift is down, prompt for file name
  1583.   jz Ret4
  1584.   mov dx, OFFSET fNameBlock
  1585.   call GetFileName
  1586.   jnz Ret4                      ;If null string returned, abort
  1587.   mov si, OFFSET cancelledMsg
  1588.   jmp Abort
  1589.  
  1590. Shifted?:
  1591.   mov ah, 2                     ;Get shift key status
  1592.   int 16h
  1593.   and al, 3
  1594. Ret4:
  1595.   ret
  1596.  
  1597. EmptyBuffer:
  1598. ;If Shifted, write block buffer to file, else discard
  1599.   mov bp, [blockPtrsLast]       ;Abort if buffer is empty
  1600.   cmp bp, OFFSET blockPtrs
  1601.   jne @@L0
  1602.   jmp Beep
  1603. @@L0:
  1604.   call Shifted?                 ;If shifted, write to file
  1605.   je @@L1
  1606.   push bx
  1607.   mov dx, OFFSET fNameBlock
  1608.   call GetFileName
  1609.   mov si, OFFSET blockPtrs
  1610.   mov bx, bp
  1611.   dec bx
  1612.   dec bx
  1613.   call WriteBlock
  1614.   pop bx
  1615. @@L1:
  1616.   mov ax, OFFSET blockPtrs      ;Else just reset buffer pointer
  1617.   mov [blockPtrsLast], ax
  1618. @@Lx:
  1619.   ret
  1620.  
  1621. WriteBlock:
  1622. ;Write block to file.  SI -> starting seg pointer, BX -> ending seg pointer.
  1623.   push [lastLine]               ;Copy block buffered lines to file:
  1624.   push [fHandle]
  1625.   mov ah, 3Ch                   ;CREATe file
  1626.   sub cx, cx
  1627.   mov dx, OFFSET fNameBlock
  1628.   int 21h
  1629.   jnc @@L1
  1630.   jmp CantOpen
  1631. @@L1:
  1632.   mov [fHandle], ax             ;Write it
  1633.   mov [lastLine], bx
  1634.   mov bx, si
  1635.   call WriteFile
  1636.   mov bx, [fHandle]             ;Close it
  1637.   mov ah, 3Eh
  1638.   int 21h
  1639.   pop [fHandle]
  1640.   pop [lastLine]
  1641.   ret
  1642.  
  1643. Copy:
  1644. ;Copy marked lines, to file if shift is down, otherwise to buffer.
  1645.   test [marking?], -1          ;Abort with a beep if not already marking,
  1646.   jnz @@L1
  1647.   mov si, OFFSET notMarkingMsg
  1648.   jmp Abort
  1649. @@L1:
  1650.   push bx
  1651.   push di
  1652.   push es
  1653.   mov si, [mark]
  1654.   cmp bx, si                    ;If mark comes after here, exchange
  1655.   jae @@L2
  1656.   xchg bx, si
  1657. @@L2:
  1658.   mov [mark], si                ; save in this order for possible delete
  1659.   call FileOrBuffer?            ;If Shift key was down when command entered,
  1660.   je @@L4
  1661.   call WriteBlock
  1662.   mov di, OFFSET blockPtrs
  1663.   jmp @@Lx
  1664. @@L4:
  1665.   mov cx, bx                    ;If no Shift, move marked lines to buffer
  1666.   sub cx, si
  1667.   shr cx, 1
  1668.   inc cx
  1669.   mov di, OFFSET blockPtrs
  1670.   mov ax, ds
  1671.   mov es, ax
  1672.   rep movsw
  1673. @@Lx:
  1674.   mov [blockPtrsLast], di       ;Save pointer to last line
  1675.   pop es
  1676.   pop di
  1677.   pop bx
  1678.   mov [marking?], 0
  1679.   ret
  1680.  
  1681. DeleteBlock:
  1682. ;Do a Copy, then delete copied lines
  1683.   call Copy                     ;Copy block to file or buffer
  1684.   mov si, bx                    ;Close up copied lines:
  1685.   inc si                        ;SI = cursor line + 2
  1686.   inc si
  1687.   mov di, [mark]                ;DI = start of marking
  1688.   mov cx, [lastLine]            ;CX = number of lines from here to end
  1689.   sub cx, bx
  1690.   push es
  1691.   mov ax, ds
  1692.   mov es, ax
  1693.   rep movsb
  1694.   pop es
  1695.   dec di
  1696.   dec di
  1697.   mov [lastLine], di            ;Store index of new last line
  1698.   sub di, di                    ;Point cursor to start of old marked line
  1699.   mov bx, [mark]
  1700.   mov [needCopies?], 0
  1701.   mov [changed?], -1
  1702.   jmp NewLine
  1703.  
  1704. Find:
  1705. ;Prompt for and find next <string>.  If Shifted, reuse last <string>.
  1706.   push bx
  1707.   push es
  1708.   push di
  1709.   call Right
  1710.   test [autoReplace?], -1       ;If doing replace all, bypass shift check
  1711.   js @@L0
  1712.   jnz @@L1
  1713.   call Shifted?                 ;If Shifted,
  1714.   jnz @@L1
  1715. @@L0:
  1716.   mov si, OFFSET findMsg        ;Get search string
  1717.   mov dx, OFFSET findString
  1718.   call GetCountedString
  1719. @@L1:
  1720.   mov si, OFFSET findString
  1721.   lodsw
  1722.   dec al
  1723.   mov dl, al
  1724.   mov al, ah
  1725.   mov cx, LINELENGTH
  1726.   sub cx, di
  1727. FindLoop:
  1728.   repne scasb                   ;Scan for first char of Find string
  1729.   jne FindNextLine
  1730.   push di                       ;Once found, compare rest of string
  1731.   mov dh, cl
  1732.   mov cl, dl
  1733.   mov si, OFFSET findString + 2
  1734.   repe cmpsb
  1735.   je Found
  1736.   pop di                        ;Match failed.  Scan again for 1st char.
  1737.   mov cl, dh
  1738.   jmp FindLoop                                             
  1739. FindNextLine:
  1740.   inc bx                        ;Search next line (until EOF)
  1741.   inc bx
  1742.   mov es, [bx]
  1743.   sub di, di
  1744.   mov cl, LINELENGTH
  1745.   cmp bx, [lastLine]
  1746.   jbe FindLoop
  1747.   mov si, OFFSET notFoundMsg    ;Not found
  1748.   test [autoReplace?], -1       ;If doing auto-replace, return
  1749.   jz @@L1
  1750.   mov [autoReplace?], 0
  1751.   pop di
  1752.   pop es
  1753.   pop bx
  1754.   ret
  1755. @@L1:
  1756.   call Print0                   ;Else restore cursor, abort with error message
  1757.   pop di
  1758.   pop es
  1759.   pop bx
  1760.   jmp na
  1761. Found:
  1762.   pop di
  1763.   dec di
  1764.   add sp, 6
  1765.   mov [justFound?], -1
  1766.   mov ax, bx                    ;Show found line 5 below top of screen
  1767.   jmp JL1
  1768.  
  1769. GetCountedString:
  1770. ;Print prompt string at [SI], read counted string into [DX].
  1771. ;Returns count (minus CR/LF) in AL.  DX is advanced one to start of string.
  1772.   push di
  1773.   push dx
  1774.   call Prompt                   ;Display prompt
  1775.   pop dx
  1776.   mov di, dx
  1777.   inc dx
  1778.   call GetString                ;Get input string
  1779.   mov [di], al                  ;Store count in front of string
  1780.   pop di
  1781.   ret
  1782.  
  1783. Replace:
  1784.   push di
  1785.   test [autoReplace?], -1       ;If using auto-replace command,
  1786.   jz @@L0
  1787.   jns @@L2                      ;Skip shift check
  1788.   mov [autoReplace?], 1
  1789.   jmp SHORT @@L1a               ;Get replace string if shifted and first pass
  1790. @@L0:
  1791.   test [justFound?], -1         ;Beep if not immediately preceded by a Find
  1792.   jnz @@L1
  1793. @@LE:
  1794.   pop di
  1795.   jmp na
  1796. @@L1:
  1797.   call Shifted?                 ;If not Shifted, prompt for replace string
  1798.   jnz @@L2
  1799. @@L1a:
  1800.   mov si, OFFSET replaceMsg
  1801.   mov dx, OFFSET replaceString
  1802.   call GetCountedString
  1803. @@L2:
  1804.   mov si, OFFSET replaceString
  1805.   lodsb
  1806.   sub ah, ah
  1807.   mov dx, si
  1808.   push ax
  1809.   push dx
  1810.   sub ch, ch                    ;Compare lengths of find and replace strings
  1811.   mov cl, al
  1812.   sub cl, [byte findString]     ;If replace string is longer,
  1813.   je @@L6
  1814.   jb @@L4
  1815.   xchg dx, di
  1816.   call EndLine                  ; make sure there will be enough room on line
  1817.   xchg dx, di
  1818.   add dl, cl
  1819.   cmp dl, LINELENGTH
  1820.   ja @@LE
  1821. @@L3:
  1822.   call Insert0                  ; then insert extra characters
  1823.   loop @@L3
  1824.   jmp SHORT @@L6
  1825. @@L4:
  1826.   neg cl                        ;If shorter, delete difference
  1827. @@L5:
  1828.   call Delete0
  1829.   loop @@L5
  1830. @@L6:
  1831.   pop si                        ;Now copy new string over old
  1832.   pop cx
  1833.   sub ch, ch
  1834.   pop di
  1835.   rep movsb
  1836.   mov [changed?], -1
  1837.   jmp NewLine
  1838.  
  1839. AutoReplace:
  1840. ;Find and replace all.  If shift not down, prompt for strings.
  1841.   push [top]
  1842.   push es
  1843.   push di
  1844.   push bx
  1845.   mov [autoReplace?], 1         ;Set flag: 1 = no shift, -1 = shift
  1846.   call Shifted?
  1847.   jne @@L1
  1848.   mov [autoReplace?], -1
  1849. @@L1:
  1850.   call Find                     ;Find/replace until flag reset by not found
  1851.   test [autoReplace?], -1
  1852.   jz @@Lx
  1853.   call Replace
  1854.   jmp @@L1
  1855. @@Lx:
  1856.   pop bx                        ;Restore original position
  1857.   pop di
  1858.   pop es
  1859.   pop [top]
  1860.   push si
  1861.   call ReDraw                   ;Refresh screen with any changes
  1862.   pop si
  1863.   push di
  1864.   call Print0                   ;Show notFoundMsg and exit
  1865.   pop di
  1866.   jmp NextNoRedraw
  1867.  
  1868. Help:
  1869.   push es
  1870.   push di
  1871.   mov es, [videoSegment]
  1872.   mov di, 160
  1873.   mov si, OFFSET helpMsg
  1874.   mov cx, 80 * 24
  1875.   mov ah, ATTRIB_NL
  1876. @@L1:
  1877.   lodsb
  1878.   stosw
  1879.   loop @@L1
  1880.   mov si, OFFSET endHelpMsg
  1881.   call Print0
  1882.   sub ah, ah
  1883.   int 16h
  1884.   pop di
  1885.   pop es
  1886.   ret
  1887.  
  1888. END
  1889.  
  1890.